package com.codemonkey.lq.qd.facotry.support;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.reflection.property.PropertyCopier;

import com.codemonkey.lq.qd.ServiceContext;
import com.codemonkey.lq.qd.facotry.config.AbstractActionDefinition;
import com.codemonkey.lq.qd.facotry.config.ActionDefinition;
import com.codemonkey.lq.qd.facotry.config.ExecuteContextModifier;
import com.codemonkey.lq.qd.facotry.config.ExecutionEvent;
import com.codemonkey.lq.qd.facotry.config.FlowDefinition;
import com.codemonkey.lq.qd.facotry.config.ServiceDefinition;
import com.codemonkey.lq.qd.facotry.config.event.ExecutionEventObjectFactory;
import com.codemonkey.lq.qd.utils.ExpressionEvaluatorUtils;

/**
 * 本Action会链接到它可能会执行到的几个action
 * 然后推动整个action链条向前执行.
 * 参考了Tomcat中的Valve.本Valve持有执行完本Valve之后将要执行的Valve.执行下一个Valve的权利被框架下方给某个Valve.从而推动整个Pipeline向前执行.
 * 用于处理包含<flow-list>的Service
 * TODO 画一个图,以图形的方式展示出其链接的所有的ActionDefinition,便于调试XMLServiceBuilderPostProcessor重构的Action链条.
 * 
 * 
 * -----------------------------------------------------------------------------------------------------------------------------------
	<flow-list>
		<flow from="start" to="step1"/>
		<flow from="step1" to="step2">
			<![CDATA[
				${selectResult.age} == 30
			]]>
		</flow>
		<flow from="step1" to="step3" />
		<flow from="step3" to="end" />
		<flow from="step2" to="end" />
	</flow-list>
	
	#
	[ start -- step1 -- [next AD is [ step2 ] ,and condition is ${selectResult.age} == 30, next AD is [ step3 ] ,and condition is 1 == 1]
	                    [next AD is [ end ] ,and condition is 1 == 1]                        [next AD is [ end ] ,and condition is 1 == 1]
						
	
	# 因为条件 1==1 是自己添加的,保证绝对通过;所以在下面的图表中没必要列出来					
	start
	  |
	   —— step1(把到每个节点执行后拥有的数据也列举出来,尤其是和之后判断相关的信息;可单独列出来)
			|
			|
			|   (condition is '${selectResult.age} == 30') 
			 ——————————————————————————————————————————————— step2 
			|                                                  |
			|                                                   —— end
			|      
			 —— step3 
				  | 
				   —— end
   ----------------------------------------------------------------------------------------------------------------------------------- 
 * @author LQ
 *
 */
public class LinkedActionDefinition extends GenericActionDefinition { // implements Printer

	private List<Condition> conditions = new ArrayList<Condition>(10);

	public static LinkedActionDefinition of_START_AD(ActionDefinition startAD,
			ServiceDefinition parentSD) {
		LinkedActionDefinition ad = new LinkedActionDefinition(startAD, parentSD);
		ad.setName(FlowDefinition.START);
		//		ad.setLevel(0);
		return ad;
	}

	public static LinkedActionDefinition of_END_AD(ActionDefinition endAD,
			ServiceDefinition parentSD, LinkedActionDefinition parentAD) {
		LinkedActionDefinition ad = new LinkedActionDefinition(endAD, parentSD);
		ad.setName(FlowDefinition.END);
		//		ad.setLevel(parentAD.getLevel()+1);
		//绝对无法通过的条件,所以nextAD可以设置为null
		ad.addNextAD("1 == 2", null);
		return ad;
	}

	private LinkedActionDefinition(ActionDefinition innerAD, ServiceDefinition sd) {
		super(sd);
		//不再维持内部的innerAD 直接启用LinkedActionDefinition(用于<flow-list/>的情况),GenericActionDefinition用于没有<flow-list/>的情况
		if (null == innerAD) {
			//TODO 先这样,后期想想这个到底放在哪里... 2016-10-27 20:33
			return;
		}

		PropertyCopier.copyBeanProperties(AbstractActionDefinition.class, innerAD, this);
		//this.setCondition(innerAD.getCondition());
		//this.setHandler(innerAD.getHandler());
		//this.setName(innerAD.getName());
		//this.setPage(innerAD.isPage());
		//this.setParameter(innerAD.getParameter());
		//this.setResult(innerAD.getResult());
		//this.setResultType(innerAD.getResultType());
		//this.setSqlId(innerAD.getSqlId());
		//this.setSqlType(innerAD.getSqlType());

	}

	public LinkedActionDefinition addNextAD(String condition, ActionDefinition nextAD) {
		if (conditions.contains(condition)) {
			throw new IllegalArgumentException("it has alreay one same condtion which named : ["
					+ condition + "], the end point is : " + nextAD.getName());
		}

		LinkedActionDefinition laDefinition = new LinkedActionDefinition(nextAD, parentSD);
		//神来之笔,自己都佩服自己... 因为自身只能添加下一步到达的ActionDefinition,所以直接加1赋值过去即可
		//		laDefinition.setLevel(this.getLevel()+1);
		conditions.add(new Condition(condition, laDefinition));
		return laDefinition;
	}

	@Override
	public void execute(ServiceContext serviceContext) throws Exception {
		if (this.getHandler() != null) {
			this.getHandler().preHandle(this, serviceContext);
		}

		doAction(serviceContext);

		if (this.getHandler() != null) {
			this.getHandler().afterHandle(this, serviceContext);
		}
		
		ExecutionEvent event =  ExecutionEventObjectFactory.getExecutionEvent(true, this, serviceContext);
		executeEventMulticaster.multicastEvent(event);

		afterExecuteSelfLogic(serviceContext);
	}

	/**
	 * 执行完自身逻辑之后
	 * @param serviceContext
	 * @throws Exception
	 */
	protected void afterExecuteSelfLogic(ServiceContext serviceContext) throws Exception {
		List<Condition> passed = getPassed(serviceContext);

		if (passed.size() == 0) {
			return;
		}

		if (passed.size() > 1) {
			throw new IllegalStateException("there are two branch :" + passed.toString());
		}

		//推动整个Action链条往前执行.
		passed.get(0).nextAD.execute(serviceContext);
	}

	/**
	 * 获取下一批可执行的Action
	 * @param sc
	 * @return
	 */
	private List<Condition> getPassed(ServiceContext sc) {
		List<Condition> passed = new ArrayList<Condition>(10);
		for (Condition cond : conditions) {
			prepareExecuteContext(sc, cond);
			boolean tof = ExpressionEvaluatorUtils.evaluateBoolean(cond.condition, sc
					.getExecuteContextContainer());
			if (!tof) {
				continue;
			} else {
				passed.add(cond);
			}
		}

		return passed;
	}

	private void prepareExecuteContext(ServiceContext sc, Condition cond) {
		ExecuteContextModifier modifier = parentSD.getConfiguration().getExecuteContextModifier();
		if (null == modifier) {
			return;
		}
		
		Map<String, Object> executeContextContainer = sc.getExecuteContextContainer();
		modifier.modify(executeContextContainer, sc, cond.nextAD);
	}

	protected static class Condition {
		private ActionDefinition nextAD;
		private String condition;

		private Condition(String condition, ActionDefinition nextAD) {
			super();
			this.nextAD = nextAD;
			this.condition = condition;
		}

		@Override
		public String toString() {
			return String.format("next AD is %s ,AND condition is %s", nextAD, condition);
		}

		@Override
		public boolean equals(Object other) {
			if (this == other) {
				return true;
			}

			if (other == null || other.getClass() != this.getClass()) {
				return false;
			}

			Condition that = (Condition) other;

			//若condtion一致,则认为相等
			return that.condition.equalsIgnoreCase(this.condition);
		}

		@Override
		public int hashCode() {

			return String.format("%s#%s", this.getClass().getName(), this.condition).hashCode();
		}
	}

	// -------------------- Implement Printer Interface --------------------------------
	// 不好搞啊！！！！
	//	private int level = 0;
	//
	//	private void setLevel(int level) {
	//		this.level = level;
	//	}
	//
	//	@Override
	//	public int getLevel() {
	//		return level;
	//	}
	//
	//	@Override
	//	public void print(Printer parentPrinter) {
	//		
	//	}
}
